#include "StdAfx.h"
#include "AdderString.h"
#include <cassert>
#include <cstdio>
#include <cstring>
#include <stdlib.h>
#include <math.h>
#include <memory>

#define min(a,b)    (((a) < (b)) ? (a) : (b))

#ifdef STRING_ALLOC_DEBUG
long int stringsAllocated = 0;
#endif

AdderString::AdderString()
{
	this->block_size = 512; // malloc is called in 400 character blocks

	this->data = (char*)malloc(this->block_size*sizeof(char));
	this->data[0] = NULL;
	this->string_length = 0;
	this->freed = false;
#ifdef STRING_ALLOC_DEBUG
	stringsAllocated += 1;
	printf("NEW          :   NULL   -> %p (%i) \"%s\"\n", this->data, stringsAllocated, this->data);
#endif
}

// copy constructor
AdderString::AdderString(const AdderString& c)
{
	this->block_size = c.block_size;
	this->string_length = c.string_length;
	this->data = (char*)malloc(this->string_length*sizeof(char));
	this->data[0] = NULL;
	strcpy(this->data,c.data);
 	this->freed = false;
#ifdef STRING_ALLOC_DEBUG
	stringsAllocated += 1;
	printf("COPY         : %p -> %p (%i) \"%s\"\n", c.data, this->data, stringsAllocated, this->data);
#endif
}

AdderString::AdderString(const char* str)
{
	this->block_size = 512; // malloc is called in 400 character blocks

	// calculate the size of the string we've been passed, rounded up
	// to the nearest this->block_size character boundary.
	// Math Note: We add 1 for the NULL character, but subtract 1 for the
	//            rounding up integer division, therefore the 1s cancel
	//            each other out.
	int ssize = (strlen(str) + 1 + (this->block_size - 1)) / this->block_size;
	ssize *= this->block_size;

	this->data = (char*)calloc(ssize,sizeof(char));
	this->string_length = ssize;
	strcpy(this->data,str);
	this->freed = false;
#ifdef STRING_ALLOC_DEBUG
	stringsAllocated += 1;
	printf("NEW          :   NULL   -> %p (%i) \"%s\"\n", this->data, stringsAllocated, this->data);
#endif
}

AdderString::~AdderString()
{
	if (!this->freed)
	{
#ifdef STRING_ALLOC_DEBUG
		stringsAllocated -= 1;
		printf("FREE (D)     : %p ->   NULL   (%i) \"%s\"\n", this->data, stringsAllocated, this->data);
#endif

		free(this->data);
		this->freed = true;
	}
}

AdderString& AdderString::operator <<(const AdderString& b)
{
	// calculate to see whether we need to allocate more space with calloc
	// or whether we can just append
	int sizreq = strlen(this->data) + strlen(b.data) + 1;
	if (sizreq <= this->string_length)
	{
		// just append :)
		strcat(this->data, b.data);
		return (*this);
	}
	else
	{
		// resize :(
		int ssize = ((strlen(this->data) + strlen(b.data) + 1) + (this->block_size - 1)) / this->block_size;
		ssize *= this->block_size;

		char * n = (char*)calloc(ssize,sizeof(char));
		this->string_length = ssize;
		memcpy(n, this->data, strlen(this->data) + 1);
		strcat(n, b.data);
#ifdef STRING_ALLOC_DEBUG
		stringsAllocated -= 1;
		printf("FREE (R)     : %p ->   NULL   (%i) \"%s\"\n", this->data, stringsAllocated, this->data);
#endif
		free(this->data);
		this->data = n;
#ifdef STRING_ALLOC_DEBUG
		stringsAllocated += 1;
		printf("NEW          :   NULL   -> %p (%i) \"%s\"\n", n, stringsAllocated, n);
#endif
		return (*this);
	}
}

AdderString& AdderString::operator <<(float b)
{
	char fConverted[33];
	assert(sprintf(fConverted,"%f",b) > 0);

	// calculate to see whether we need to allocate more space with calloc
	// or whether we can just append
	int sizreq = strlen(this->data) + 34;
	if (sizreq <= this->string_length)
	{
		// just append :)
		strcat(this->data, fConverted);
		return (*this);
	}
	else
	{
		// resize :(
		int ssize = ((strlen(this->data) + 34) + (this->block_size - 1)) / this->block_size;
		ssize *= this->block_size;

		char * n = (char*)calloc(ssize,sizeof(char));
		this->string_length = ssize;
		memcpy(n, this->data, strlen(this->data) + 1);
		strcat(n, fConverted);
#ifdef STRING_ALLOC_DEBUG
		stringsAllocated -= 1;
		printf("FREE (R)     : %p ->   NULL   (%i) \"%s\"\n", this->data, stringsAllocated, this->data);
#endif
		free(this->data);
		this->data = n;
#ifdef STRING_ALLOC_DEBUG
		stringsAllocated += 1;
		printf("NEW          :   NULL   -> %p (%i) \"%s\"\n", n, stringsAllocated, n);
#endif
		return (*this);
	}
}

AdderString& AdderString::operator <<(int b)
{
	char fConverted[33];
	assert(sprintf(fConverted,"%i",b) > 0);
	
	// calculate to see whether we need to allocate more space with calloc
	// or whether we can just append
	int sizreq = strlen(this->data) + 34;
	if (sizreq <= this->string_length)
	{
		// just append :)
		strcat(this->data, fConverted);
		return (*this);
	}
	else
	{
		// resize :(
		int ssize = ((strlen(this->data) + 34) + (this->block_size - 1)) / this->block_size;
		ssize *= this->block_size;

		char * n = (char*)calloc(ssize,sizeof(char));
		this->string_length = ssize;
		memcpy(n, this->data, strlen(this->data) + 1);
		strcat(n, fConverted);
#ifdef STRING_ALLOC_DEBUG
		stringsAllocated -= 1;
		printf("FREE (R)     : %p ->   NULL   (%i) \"%s\"\n", this->data, stringsAllocated, this->data);
#endif
		free(this->data);
		this->data = n;
#ifdef STRING_ALLOC_DEBUG
		stringsAllocated += 1;
		printf("NEW          :   NULL   -> %p (%i) \"%s\"\n", n, stringsAllocated, n);
#endif
		return (*this);
	}
}

AdderString& AdderString::operator <<(unsigned int b)
{
	char fConverted[33];
	assert(sprintf(fConverted,"%u",b) > 0);
	
	// calculate to see whether we need to allocate more space with calloc
	// or whether we can just append
	int sizreq = strlen(this->data) + 34;
	if (sizreq <= this->string_length)
	{
		// just append :)
		strcat(this->data, fConverted);
		return (*this);
	}
	else
	{
		// resize :(
		int ssize = ((strlen(this->data) + 34) + (this->block_size - 1)) / this->block_size;
		ssize *= this->block_size;

		char * n = (char*)calloc(ssize,sizeof(char));
		this->string_length = ssize;
		memcpy(n, this->data, strlen(this->data) + 1);
		strcat(n, fConverted);
#ifdef STRING_ALLOC_DEBUG
		stringsAllocated -= 1;
		printf("FREE (R)     : %p ->   NULL   (%i) \"%s\"\n", this->data, stringsAllocated, this->data);
#endif
		free(this->data);
		this->data = n;
#ifdef STRING_ALLOC_DEBUG
		stringsAllocated += 1;
		printf("NEW          :   NULL   -> %p (%i) \"%s\"\n", n, stringsAllocated, n);
#endif
		return (*this);
	}
}

AdderString& AdderString::operator <<(const char * b)
{
	// calculate to see whether we need to allocate more space with calloc
	// or whether we can just append
	int sizreq = strlen(this->data) + strlen(b) + 1;
	if (sizreq <= this->string_length)
	{
		// just append :)
		strcat(this->data, b);
		return (*this);
	}
	else
	{
		// resize :(
		int ssize = ((strlen(this->data) + strlen(b) + 1) + (this->block_size - 1)) / this->block_size;
		ssize *= this->block_size;

		//_crtBreakAlloc = 383;
		char * n = (char*)calloc(ssize,sizeof(char));
		this->string_length = ssize;
		memcpy(n, this->data, strlen(this->data) + 1);
		strcat(n, b);
#ifdef STRING_ALLOC_DEBUG
		stringsAllocated -= 1;
		printf("FREE (R)     : %p ->   NULL   (%i) \"%s\"\n", this->data, stringsAllocated, this->data);
#endif
		free(this->data);
		this->data = n;
#ifdef STRING_ALLOC_DEBUG
		stringsAllocated += 1;
		printf("NEW          :   NULL   -> %p (%i) \"%s\"\n", n, stringsAllocated, n);
#endif
		return (*this);
	}
}

AdderString& AdderString::operator +=(const AdderString& b)
{
	// calculate to see whether we need to allocate more space with calloc
	// or whether we can just append
	int sizreq = strlen(this->data) + strlen(b.data) + 1;
	if (sizreq <= this->string_length)
	{
		// just append :)
		strcat(this->data, b.data);
		return (*this);
	}
	else
	{
		// resize :(
		int ssize = ((strlen(this->data) + strlen(b.data) + 1) + (this->block_size - 1)) / this->block_size;
		ssize *= this->block_size;

		char * n = (char*)calloc(ssize,sizeof(char));
		this->string_length = ssize;
		memcpy(n, this->data, strlen(this->data) + 1);
		strcat(n, b.data);
#ifdef STRING_ALLOC_DEBUG
		stringsAllocated -= 1;
		printf("FREE (R)     : %p ->   NULL   (%i) \"%s\"\n", this->data, stringsAllocated, this->data);
#endif
		free(this->data);
		this->data = n;
#ifdef STRING_ALLOC_DEBUG
		stringsAllocated += 1;
		printf("NEW          :   NULL   -> %p (%i) \"%s\"\n", n, stringsAllocated, n);
#endif
		return (*this);
	}
}

bool AdderString::operator ==(const AdderString& b) const
{
	return (strcmp(this->data, b.data) == 0);
}

bool AdderString::operator ==(const char * b) const
{
	return (strcmp(this->data, b) == 0);
}

bool AdderString::operator !=(const AdderString& b) const
{
	return (strcmp(this->data, b.data) != 0);
}

bool AdderString::operator !=(const char * b) const
{
	return (strcmp(this->data, b) != 0);
}

bool AdderString::operator <(const AdderString& b) const
{
	return (strcmp(this->data, b.data) < 0);
}

bool AdderString::operator >(const AdderString& b) const
{
	return (strcmp(this->data, b.data) > 0);
}

bool AdderString::operator <=(const AdderString& b) const
{
	return (strcmp(this->data, b.data) <= 0);
}

bool AdderString::operator >=(const AdderString& b) const
{
	return (strcmp(this->data, b.data) >= 0);
}

AdderString& AdderString::operator =(const AdderString& c)
{
	// Assignment
	if (!this->freed)
	{
		// free the existing data
#ifdef STRING_ALLOC_DEBUG
		stringsAllocated -= 1;
		printf("FREE (A)     : %p ->   NULL   (%i) \"%s\"\n", this->data, stringsAllocated, this->data);
#endif
		free(this->data);
	}
	
	// now copy the stuff
	this->block_size = c.block_size;
	this->string_length = c.string_length;
	//_crtBreakAlloc = 448;
	this->data = (char*)malloc(this->string_length*sizeof(char));
	this->data[0] = NULL;
	strcpy(this->data,c.data);
 	this->freed = false;
#ifdef STRING_ALLOC_DEBUG
	stringsAllocated += 1;
	printf("ASSIGN       : %p -> %p (%i) \"%s\"\n", c.data, this->data, stringsAllocated, this->data);
#endif
	(const_cast<AdderString&>(c)).freed = true;
	free(c.data); // does this work?
#ifdef STRING_ALLOC_DEBUG
	stringsAllocated -= 1;
#endif
	return (*this);
}

const char* AdderString::c_str()
{
	return this->data;
}

std::auto_ptr<char> AdderString::substr(unsigned int start, unsigned int count)
{
	char* result = (char*)malloc(count + 1);
	unsigned int realCount = min(strlen(this->data), start + count);
	result = (char*)memcpy(result, (this->data + sizeof(char)*start), realCount);
	result[realCount - 1] = NULL; //string termination

	std::auto_ptr<char> smart_return(result);
	return smart_return;
	//AdderString * p = new AdderString(result);
	//return (*p);
}

char AdderString::at(unsigned int pos)
{
	if (this->string_length < pos) return -1;
	return this->data[pos];
}

unsigned int AdderString::size()
{
	return strlen(data);
}

unsigned int AdderString::memsize()
{
	return this->string_length;
}